package 使用日志;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.util.StatusPrinter;

//import ch.qos.logback.classic.BasicConfigurator;
public class SLF4J和Logback {
	public static void main(String[] args) {
		System.out.println("aaaaaaaa");
      Logger logger = LoggerFactory.getLogger("SLF4J和Logback");
      logger.info("hello world,this is a debug message...");
    
    	// 打印内部的状态
      LoggerContext loggerContext = (LoggerContext)LoggerFactory.getILoggerFactory();
      StatusPrinter.print(loggerContext);
/*    TRACE：最详细的日志级别，用于跟踪方法调用和变量值等详细信息。
      DEBUG：用于调试应用程序，输出有用的调试信息。
      INFO：用于记录应用程序的运行状态和关键事件。
      WARN：用于记录潜在的问题或异常情况，但不会影响应用程序的正常运行。
      ERROR：用于记录应用程序中的错误或异常情况，这些情况可能导致应用程序崩溃或无法正常运行。
      OFF：用于关闭日志记录。
      （TRACE < DEBUG < INFO < WARN < ERROR）如果一条的日志的打印等级大于等于 Logger实例 的等级，该条日志才可以被打印出来。
【Logger的等级】

Logger 实例被分成不同的等级，不同的等级定义在 ch.qos.logback.classic.Level 类中。

等级分类有 （TRACE < DEBUG < INFO < WARN < ERROR）

每一个获取到的 Logger实例如果没有指定等级，那么它会继承离它最近的一个祖先的等级。

ROOT 实例默认等级为 DEBUG ，倘若其他Logger实例都没有指定等级，那么继承ROOT的DEBUG等级。

如果实例本身指定了等级，那么就直接用自身的等级，不用继承。
示例：
logger名字    指定层级       有效层级
root         debug        debug
x            info         info
x.y          none         info
x.y.z        error        error
*/

  }
}

/*
 从目前的趋势来看，越来越多的开源项目从Commons Logging加Log4j转向了SLF4J加Logback
 
SLF4J和Logback

在我们开发Java应用程序时，了解日志框架是非常重要的。在应用程序中添加日志记录可以帮助我们诊断问题并进行性能优化。在这篇博客中，我主要介绍了两个常见的Java日志框架SLF4J和Logback。深入的介绍了SLF4J框架常见的用法和最佳实践，以及Logback框架的基本使用，包括日志输出级别、输出到文件、滚动策略和异步日志等。此外还分析了Logback框架的核心模块和三个主要类。

常见框架介绍
在Java中，日志框架非常重要，可以用来记录应用程序的状态和行为，方便开发人员排查问题。常见的Java日志框架有Java自带的java.util.logging（JUL）、Apache Commons Logging（Commons Logging）、Log4j、Log4j2和Logback等。而SLF4J则是一个日志门面，它并不是具体的日志实现，而是提供了一套统一的接口，方便开发人员在不同的日志实现之间切换。

下面我们对上述几个日志框架做一个简单的介绍：

1.Java自带的java.util.logging（JUL）
JUL是Java标准库中自带的一个简单的日志框架，它提供了一组API来记录日志。JUL的API简单，易于使用，并且可以轻松地集成到Java应用程序中。但是，它的配置和扩展能力较差，不太适合复杂的应用程序。

2.Apache Commons Logging（Commons Logging）
Commons Logging是Apache软件基金会提供的一个日志框架，它是JCL和SLF4J的前身。它提供了一种抽象的日志接口，并支持多个日志实现，包括JUL、Log4j和Logback等。Commons Logging的API简单，但是配置和使用过程中需要注意一些细节。

3.Log4j
Log4j是Apache软件基金会提供的一个流行的日志框架，它支持多种输出格式和日志级别，并提供了多种日志输出目标，如文件、控制台、邮件等。Log4j的API简单易用，但是配置比较复杂，需要熟悉它的配置文件格式。

4.Log4j2
Log4j2是Log4j的下一代版本，它解决了Log4j在性能、灵活性和可扩展性方面的一些问题。Log4j2使用异步日志记录器，提供了更好的性能，并支持多线程和异步日志记录。与Log4j相比，Log4j2的配置更加简单易用。

5.Logback
Logback是一个高性能的日志框架，由Log4j的创始人Ceki Gülcü开发。Logback支持多种日志级别、多种输出格式和输出目标，并提供了异步日志记录器和滚动日志文件等功能。Logback的配置文件格式与Log4j2类似，但是更加简洁。

6.SLF4J
SLF4J（Simple Logging Facade for Java）是一个日志门面，它并不是具体的日志实现，而是提供了一套统一的接口。开发人员只需要编写SLF4J的API代码，而
不需要直接调用具体的日志实现，这样就可以轻松地在不同的日志实现之间切换。SLF4J可以和Log4j、Logback、JUL和Commons Logging等日志框架配合使用，方便
开发人员根据项目需求选择最合适的日志实现。

SLF4J概念：简单日志门面，对应的英文为Simple Logging Facade，是存取日志的标准接口。

也就是说它仅仅是一个日志输出的接口，并不是一种具体的实现方案，就像 JDBC一样只是统一的接口。

想要使用必须搭配其他的日志实现方案，如：log4j，logback，JDK logging 等等

SLF4J的优点在于它提供了统一的接口，这样就可以在不同的日志框架之间切换，而不需要修改代码。它还提供了一些方便的特性，例如占位符、MDC（Mapped Diagnostic Context）等。此外，SLF4J还提供了一个简单的桥接器，可以将JUL、Commons Logging和Log4j等日志框架与SLF4J集成，这使得开发人员可以轻松地将这些日志框架迁移到SLF4J上。

SLF4j
1. 使用SLF4J的API
在代码中，我们可以使用SLF4J的API来记录日志。SLF4J的API包含一个Logger接口，我们可以通过SLF4J的LoggerFactory类获取Logger实例。 例如：

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyClass {
    private static final Logger logger = LoggerFactory.getLogger(MyClass.class);

    public void doSomething() {
        logger.info("doing something");
    }
}
在上面的代码中，我们通过LoggerFactory.getLogger()方法获取了一个Logger实例，并使用logger.info()方法记录了一条日志。

2. 配置日志实现库
接下来，我们需要在项目中配置具体的日志实现库。对于Logback，我们需要创建一个名为logback.xml的配置文件，并将其放置在classpath下。 例如，我们可以创建一个这样的配置文件：

<configuration>
  <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="info">
    <appender-ref ref="console"/>
  </root>
</configuration>
在上面的配置文件中，我们定义了一个名为console的控制台输出器，同时指定了输出的格式。然后，我们将console输出器添加到了root日志记录器中。

如果配置文件 logback-test.xml 和 logback.xml 都不存在，那么 logback 默认地会调用BasicConfigurator ，创建一个最小化配置。最小化配置由一个关联
到根 logger 的ConsoleAppender 组成。输出用模式为%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 的 PatternLayoutEncoder 进
行格式化。root logger 默认级别是 DEBUG。
1、Logback的配置文件：
	Logback 配置文件的语法非常灵活。正因为灵活，所以无法用 DTD 或 XML schema 进行定义。尽管如此，可以这样描述配置文件的基本结构：以开头，后面有零个或多个元素，有
	零个或多个元素，有最多一个元素。
2、Logback默认配置的步骤：
　　(1). 尝试在 classpath下查找文件logback-test.xml；
　　(2). 如果文件不存在，则查找文件logback.xml；
　　(3). 如果两个文件都不存在，logback用BasicConfigurator自动对自己进行配置，这会导致记录输出到控制台。


3. SLF4J的特性
SLF4J提供了一些方便的特性，例如占位符、MDC等。

占位符
logger.info("processing order {} for customer {}", orderId, customerId);
在上面的代码中，{}表示一个占位符，这些占位符将在记录日志时被实际的值所替换。例如，如果orderId为123，customerId为456，则记录的日志内容为：

processing order 123 for customer 456
MDC（Mapped Diagnostic Context）
MDC是SLF4J提供的一种上下文信息管理机制。我们可以使用MDC将一些上下文信息添加到日志中，例如请求ID、用户ID等。 使用MDC需要先调用MDC.put(key, value)方法将信息添加到上下文中，然后在记录日志时使用%X{key}的方式引用这些信息。 例如：

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

public class MyClass {
    private static final Logger logger = LoggerFactory.getLogger(MyClass.class);

    public void doSomething(String requestId, String userId) {
        MDC.put("requestId", requestId);
        MDC.put("userId", userId);

        logger.info("doing something");

        MDC.remove("requestId");
        MDC.remove("userId");
    }
}
在上面的代码中，我们在调用doSomething()方法时将请求ID和用户ID添加到了MDC中。在记录日志时，我们可以使用%X{requestId}和%X{userId}的方式引用这些信息。 最佳实践是使用SLF4J作为日志门面，这样就可以方便地切换不同的日志实现。在使用SLF4J时，需要注意以下几点：

导入SLF4J的API和具体的日志实现库。
在代码中使用SLF4J的API，例如Logger接口。
在配置文件中配置具体的日志实现，例如Log4j或Logback。
避免直接调用具体的日志实现，应该始终使用SLF4J的API。
尽量避免在循环中记录日志，这样会降低程序的性能。
对于频繁发生的日志记录，应该使用异步日志记录器，以提高程序的性能。
SLF4J是一个很好的日志门面，它提供了统一的接口，方便开发人员在不同的日志实现之间切换。在使用SLF4J时，需要注意一些细节，例如导入API和具体的日志实现库、使用SLF4J的API、配置具体的日志实现、避免直接调用具体的日志实现等。

在Java中，有许多日志框架可用，其中最受欢迎的是Logback。Logback是由Ceki Gülcü开发的，是log4j框架的升级版，是一个可靠且高效的日志框架，广泛应用于Java应用程序中。

Logback的基本配置
Logback是log4j框架的升级版，是一个可靠且高效的日志框架，内置在Springboot框架中。 使用Logback记录日志非常简单，只需要按照以下步骤进行配置：

将Logback添加到项目的依赖项中。这可以通过Maven或Gradle等构建工具完成。
创建一个名为logback.xml的配置文件。这个文件包含了Logback的所有配置信息。
在logback.xml文件中，配置日志的格式、输出方式、日志级别等信息。
基本配置
logback.xml配置文件结构主要为configuration下包含零个或者多个appender元素，后面跟着零个或者多个logger元素，后面跟着最多一个root元素。

<configuration>
appender
logger
root
下面是一个基本的logback.xml文件示例：

<configuration>

  <!-- 配置Console Appender -->
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <!-- 配置File Appender -->
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>/var/log/myapp.log</file>
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <!-- 配置Logger -->
  <logger name="com.example.myapp" level="DEBUG" additivity="false">
    <appender-ref ref="CONSOLE" />
    <appender-ref ref="FILE" />
  </logger>

  <!-- 配置Root logger -->
  <root level="WARN">
    <appender-ref ref="CONSOLE" />
    <appender-ref ref="FILE" />
  </root>

</configuration>
这个配置文件中定义了一个名为CONSOLE的ConsoleAppender，用于将日志输出到控制台。encoder元素定义了日志格式。root元素定义了日志的默认级别为INFO。

配置输出到文件
除了控制台输出，还可以将日志输出到文件中。为此，需要使用FileAppender和RollingFileAppender。

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>myapp.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>myapp-%d{yyyy-MM-dd}.log</fileNamePattern>
        <maxHistory>30</maxHistory>
    </rollingPolicy>
    <encoder>
        <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender>
上述配置文件中，RollingFileAppender用于将日志输出到文件中。fileNamePattern指定了日志文件名的格式，maxHistory指定了日志文件的最大保存天数。

配置日志级别
在Logback中，可以使用以下日志级别：

TRACE：最详细的日志级别，用于跟踪方法调用和变量值等详细信息。
DEBUG：用于调试应用程序，输出有用的调试信息。
INFO：用于记录应用程序的运行状态和关键事件。
WARN：用于记录潜在的问题或异常情况，但不会影响应用程序的正常运行。
ERROR：用于记录应用程序中的错误或异常情况，这些情况可能导致应用程序崩溃或无法正常运行。
OFF：用于关闭日志记录。 可以通过在root元素中设置level属性来设置默认日志级别。例如：
<root level="INFO">
  <appender-ref ref="CONSOLE" />
  <appender-ref ref="FILE" />
</root>
上述配置表示将日志级别设置为INFO，并将日志输出到控制台和文件中。 可以为特定的包或类设置不同的日志级别。例如，如果想要将com.example包下的日志级别设置为DEBUG，可以这样配置： 如果只想为特定的类设置日志级别，可以在logger元素中使用类的全名。例如：
默认情况下，Logback会将日志输出到一个单独的文件中。当文件大小达到指定大小时，Logback将停止记录新的日志。为了解决这个问题，可以使用滚动策略，以便在达到指定大小时，将日志记录到不同的文件中。 在Logback中，有两种滚动策略可用：

日志滚动策略
SizeBasedTriggeringPolicy：基于文件大小触发滚动。
TimeBasedRollingPolicy：基于时间触发滚动。
这两种滚动策略都可以与RollingFileAppender一起使用。

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  <file>myapp.log</file>
  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    <fileNamePattern>myapp-%d{yyyy-MM-dd}.log</fileNamePattern>
    <maxHistory>30</maxHistory>
  </rollingPolicy>
  <encoder>
    <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
  </encoder>
</appender>
上述配置表示将日志输出到myapp.log文件中，使用TimeBasedRollingPolicy滚动策略。在这种策略下，日志文件将以myapp-YYYY-MM-dd.log的格式进行命名，其中YYYY-MM-dd是日志记录的日期。maxHistory属性指定了日志文件的最大保存天数。

配置异步日志
Logback支持异步日志，这意味着应用程序线程不必等待将日志消息写入磁盘。相反，它可以将消息放入队列中，让专门的线程负责将消息写入磁盘。这可以大大提高应用程序的性能，特别是在高并发场景下。 下面是示例：

<configuration>

  <!-- 配置异步Appender -->
  <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
    <queueSize>1024</queueSize>
    <discardingThreshold>0</discardingThreshold>
    <appender-ref ref="FILE" />
  </appender>

  <!-- 配置文件Appender -->
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>/var/log/myapp.log</file>
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{35} - %msg%n</pattern>
    </encoder>
  </appender>

  <!-- 配置Root logger -->
  <root level="info">
    <appender-ref ref="ASYNC" />
  </root>

</configuration>
解释： 在上面的配置中，我们首先定义了一个名为ASYNC的异步Appender，它使用了AsyncAppender类。queueSize属性指定了消息队列的大小，discardingThreshold属性指定了当消息队列满时应该丢弃多少消息。在本例中，我们将其设置为0，表示不会丢弃任何消息。appender-ref元素指定了实际的Appender，这里我们将其设置为FILE，表示将日志消息写入文件中。 接下来，我们定义了一个名为FILE的文件Appender，它使用了FileAppender类。file属性指定了日志文件的路径，encoder元素指定了日志消息的格式。 最后，我们将ASYNCAppender添加到了Root logger中，这意味着所有的日志消息都会被异步地写入文件中。 需要注意的是，在使用异步日志时，我们应该适当地调整消息队列的大小和丢弃阈值，以达到最佳的性能和稳定性。如果消息队列过小，可能会导致消息丢失或线程阻塞。如果丢弃阈值过高，可能会导致应用程序出现内存泄漏或崩溃。 **疑问：**File Appender中添加Async Appender是否可行？ 回答：不可以。在Logback中，Async Appender是一种特殊的Appender，它不能直接用于写日志消息，而是需要将其与其他Appender组合使用。这个异步Appender的实际作用是将日志消息放入队列中，让专门的线程负责将消息写入磁盘，而不是直接将消息写入文件中。

最佳实践
以下是使用Logback时的最佳实践：

使用适当的日志级别，以便在需要时能够识别和解决问题。在开发和测试阶段，建议使用DEBUG日志级别，以便更容易地诊断问题。在生产环境中，建议使用INFO或更高级别的日志级别，以避免日志文件变得过于庞大。
避免在代码中直接使用System.out.println()或System.err.println()进行调试和日志记录。这些方法会将日志记录到标准输出和标准错误流中，而不是日志文件中。相反，应该使用日志框架提供的API来记录日志。
了解并使用适当的日志滚动策略。这有助于确保日志文件不会变得过大，并且可以方便地查看历史日志记录。
使用合适的日志输出格式。日志输出格式应该包括有用的信息，如时间戳、线程名称、日志级别和日志信息。使用自定义格式可以使日志更容易阅读和分析。
避免记录敏感信息。在日志中记录敏感信息（如密码、密钥、个人身份信息等）可能会导致安全问题。建议使用安全的方式处理和存储敏感信息。
根据需要进行调整。日志记录应该根据需要进行调整，以便更好地反映应用程序的状态和行为。如果日志记录太少，可能会导致难以诊断问题；如果日志记录太多，可能会降低性能并使日志文件变得过大。
Logback框架解读
三个核心模块
Logback的架构包括三个核心模块：logback-core，logback-classic 和 logback-access。

logback-core
logback-core是Logback的核心模块，提供了实现日志系统所需的基本组件和框架。其中包括Logger、Appender和Layout三个主要类，它们构成了Logback的基本架构。

logback-classic
logback-classic是logback-core的扩展模块，是logback-core的一个完整实现，同时它也是SLF4J的实现。logback-classic提供了一些特有的特性，例如：自动重载配置文件、错误上下文跟踪、异步日志记录等。

logback-access
logback-access是Logback的另一个扩展模块，提供了Web应用程序的访问日志功能。它可以通过配置文件将HTTP请求和响应信息记录到日志中，以方便问题定位和性能优化。其实就是与Servlet容器集成以提供http访问日志功能。

三个主要类
Logback中的三个主要类是Logger、Appender和Layout。 Logger类是 logback-classic 模块的一部分。另一方面，Appender和Layout接口是 logback-core 的一部分。作为通用模块，logback-core 没有 Logger 的概念。

1. Logger
Logger是Logback中的核心组件，用于记录应用程序的日志。Logger的功能类似于SLF4J中的Logger，它可以向不同的Appender中输出日志信息。用来配置某个包或者类具体日志的打印级别。 xml配置

<!--com.example.myapp目录下的文件产生的日志全部放到CONSOLE和FILE这两个appender中-->
<!--默认的日志级别是DEBUG-->
<!--additivity=false表示如果能匹配到这条规则就不用往上继续查找到root节点去，防止打印多次-->
<!-- 配置Logger -->
<logger name="com.example.myapp" level="DEBUG" additivity="false">
  <appender-ref ref="CONSOLE" />
  <appender-ref ref="FILE" />
</logger>
解释： name：声明某个类或者包的日志 level：ch.qos.logback.classic.Level类中定义了一组可能的级别(TRACE，DEBUG，INFO，WARN 和 ERROR)。如果未为给定的 Logger 分配一个级别，则它将从其最接近的祖先那里继承一个已分配的级别 additivity：作用在于 children-logger是否使用 rootLogger配置的appender进行输出。

false：表示只用当前logger的appender-ref。
true：表示当前logger的appender-ref和rootLogger的appender-ref都有效。
2. Appender
Appender用于将日志信息输出到不同的目的地即负责写日志，例如控制台、文件、数据库等。Logback提供了多种类型的Appender，包括ConsoleAppender、FileAppender、SMTPAppender等。 xml配置：

  <!-- 配置Console Appender -->
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <!-- 配置File Appender -->
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>/var/log/myapp.log</file>
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>
3. Layout
Layout用于定义日志信息的输出格式。Logback提供了多种类型的Layout，例如PatternLayout、HTMLLayout、JSONLayout等。 xml配置：

  <!-- 配置Console Appender -->
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>
解释： 在上面的代码中，我们在consoleAppender中定义了一个PatternLayout，用于输出日志信息。其中，pattern元素定义了日志信息的输出格式。

扩展接口
Logback提供了很多扩展点，可以通过扩展来实现自定义的日志功能。例如：

自定义Appender：可以通过继承AppenderBase来实现自定义的Appender。
自定义Filter：可以通过实现Filter接口来实现自定义的Filter。
自定义Layout：可以通过实现Layout接口来实现自定义的Layout。
*/